"""
连接电脑服务器的app
"""
import asyncio
import os
import socket
import time

import toga
from toga.style import Pack


class Client:
    """
    Client 类，用于与服务器进行通信

    Attributes:
        server_address (tuple): 服务器的地址和端口
        data_size (int): 数据大小
        client_socket (socket.socket): 客户端套接字
        ShowMessage (list): 存储服务器返回的消息

    Methods:
        __init__(self, server_address, data_size): 初始化客户端
        send_command(self, command): 向服务器发送命令并接收响应
        close(self): 关闭客户端套接字
        ShowLs(self): 显示服务器返回的文件列表
    """

    def __init__(self, server_address, data_size):
        """
        初始化客户端

        Args:
            server_address (tuple): 服务器的地址和端口
            data_size (int): 数据大小
        """
        # 初始化服务器地址属性
        self.server_address = server_address
        # 初始化数据大小属性
        self.data_size = data_size
        # 创建客户端套接字
        self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 初始化存储服务器返回消息的列表
        self.ShowMessage = None
        # 收到的是文件还是列表
        self.IsFile = None
        # 服务器的响应
        self.response = None
        # 文件的路径
        self.FilePath = None
        # 当前文件夹路径
        self.SavePath = None
        # 当前文件信息
        self.FileMessage = None
        # 发送的信息
        self.command = None
        #
        self.IsConnected = False

    def send_command(self, command):
        """
        向服务器发送命令

        Args:
            command (str): 要发送的命令

        Returns:
            str: 服务器的响应
        """
        self.command = command
        self.client_socket.sendall(command.encode('gbk'))
        # print(self.response)

    def GetResponse(self):
        """
        接收数据
        """
        self.response = self.client_socket.recv(self.data_size)

        # print(self.response)

    def close(self):
        """
        关闭客户端套接字
        """
        self.client_socket.close()

    async def ConnectServer(self):
        """
        连接到服务器
        """
        if not self.IsConnected:
            self.client_socket.connect(self.server_address)
            self.IsConnected = True
            print("连接到服务器")

    def ShowLs(self):
        """
        显示服务器返回的文件列表
        """
        for i, data in enumerate(self.ShowMessage):
            print(f"num:{i} name:{data[0]} size:{data[1]}")

    def DoIsFile(self):
        try:  # 尝试解析服务器的响应为文件列表
            self.ShowMessage = eval(self.response.decode('gbk'))
            if self.ShowMessage[0][0] == 'wj':
                self.IsFile = True
                return True
            else:
                self.IsFile = False
                return False
        except Exception as e:  # 解析失败，响应可能是文件
            print("判定为文件夹有错，可能是文件")
            print(e)
            self.IsFile = True
            return True

    def GetFile(self):
        """
        接收文件
        """
        self.FilePath = self.ShowMessage[1][0]
        self.FilePath = self.FilePath.replace('F:', str(self.SavePath))
        total_size = self.ShowMessage[1][1]
        self.client_socket.setblocking(False)  # 非阻塞
        # 检查路径是否存在，不存在就创建
        if not os.path.exists(os.path.dirname(self.FilePath)):
            print("路径不存在，创建路径", os.path.dirname(self.FilePath))
            os.makedirs(os.path.dirname(self.FilePath))

        with open(self.FilePath, 'wb') as file:
            received_size = 0
            while True:
                data = None
                try:
                    # time.sleep(0.1)
                    data = self.client_socket.recv(1024 * 1024)
                except socket.error as e:
                    # 非阻塞模式下，recv可能会抛出socket.error异常
                    if e.errno != socket.EWOULDBLOCK:
                        # 如果不是因为没有数据可读而抛出的异常，打印错误信息
                        print("Socket error:", e)
                        break

                if data == None and (received_size == total_size):  # 接收完成
                    break
                elif data != None and received_size != total_size:  # 接收中
                    file.write(data)
                    received_size += len(data)
                    percent = (received_size / total_size) * 100 if total_size > 0 else 0
                    print(f"\rReceiving {self.FilePath}: {percent:.2f}%", end='')
                else:  # 接收失败,但是还有数据没传完
                    time.sleep(0.1)
                    continue
            print("文件接受成功")
            self.client_socket.setblocking(True)  # 阻塞

    def reconnection(self):
        """
        重新连接
        """
        pass

    async def Main(self, SavePath, command):
        """
        主函数
        """
        self.command = command
        self.SavePath = SavePath
        if not self.IsConnected:  # 确保不会多次启动客户端
            self.ConnectServer()  # 连接到服务器
            self.IsConnected=True
        while 1:
            if self.command == '':
                print("请输入命令 (ls,../, pwd, wq): ")  # 获取客户命令
                await asyncio.sleep(1)  # 返回到任务调度,等待命令协程启动
            else:
                self.send_command(self.command)  # 发送命令并获取响应
                self.GetResponse()
                self.DoIsFile()
                if self.IsFile:  # 响应是文件
                    self.GetFile()
                else:
                    self.ShowLs()

                if self.command == "wq":
                    self.client_socket.close()
                    break
                self.command = ''#防止循环进入发送命令


class RunApp:
    """
    任务调度
    """

    def __init__(self, main_box):
        """
        构建输入框、按钮等
        :param main_box:
        """
        self.main_box = main_box
        # 服务器数据
        self.SavePath = None
        self.ServerAddress = None
        self.Port = None
        # 客户端数据
        self.Client = None
        self.Command = None
        # 客户端连接
        self.IsConnected = False

        # 文本框
        self.InServer = toga.TextInput(placeholder='服务器地址', style=Pack(flex=1))  # 服务器地址
        self.InPort = toga.TextInput(placeholder='服务器端口', style=Pack(flex=1))  # 端口号
        self.InSavePath = toga.TextInput(placeholder='保存路径', style=Pack(flex=1))  # 保存路径
        self.InCommand = toga.TextInput(placeholder='命令', style=Pack(flex=1))  # 命令
        self.InServer.value='cn-sz-yd-plustmp2.natfrp.cloud'
        self.InPort.value='10620'
        self.InSavePath.value='/data/data/com.termux/files/home/storage/shared/pc数据'
        self.InCommand.value=''

        # 显示数据
        self.Show = toga.Label('初始文本', style=Pack(flex=1))
        self.Show.text = "初始化未完成"
        # 按钮
        self.BtnConnect = toga.Button('连接服务器', on_press=self.GetServerData, style=Pack(flex=1))  # 获取服务器数据
        self.BtnCommand = toga.Button('发送命令', on_press=self.GetCommand, style=Pack(flex=1))  # 获取命令

        # 布局
        # 文本框布局
        text_box = toga.Box(style=Pack(direction='row', padding=10))
        text_box.add(self.InServer)
        text_box.add(self.InPort)
        text_box.add(self.InSavePath)
        text_box.add(self.InCommand)
        self.main_box.add(text_box)

        # 按钮布局
        button_box = toga.Box(style=Pack(direction='row', padding=10))
        button_box.add(self.BtnConnect)
        button_box.add(self.BtnCommand)
        self.main_box.add(button_box)

        self.main_box.add(self.Show)

        self.main_box.style.direction = 'column'

    # 获取服务器数据,服务器数据协程
    async def GetServerData(self, widget):
        if self.InServer.value == '' or self.InPort.value == '':
            self.Show.text = "未获取服务器数据,请输入服务器地址和端口号"
            await self.Start()  # 返回到任务调度
        else:
            self.ServerAddress = self.InServer.value
            self.Port = self.InPort.value
            self.Show.text = f"服务器地址{self.ServerAddress} 端口号{self.Port}"
            if not self.IsConnected:  # 客户端未连接
                self.Client = Client((self.ServerAddress, int(self.Port)), 4096)
                print('客户端对象建立成功')
                self.IsConnected = True
                await self.Client.ConnectServer()  # 发送服务器数据到客户端

    # 获取命令，命令协程
    async def GetCommand(self, widget):
        while 1:
            if self.InServer.value == '' or self.InPort.value == '':
                self.Show.text = "未获取服务器数据,请输入服务器地址和端口号,连接服务器后发送命令"
                await self.Start()  # 返回到任务调度
            else:
                if self.InCommand.value == '':
                    self.Show.text = "请输入命令"
                    await self.Start()  # 返回到任务调度
                else:  # 连接服务器而且有命令
                    self.Command = self.InCommand.value
                    self.Show.text = f"命令为：{self.Command}"
                    self.Client.command=self.Command
                    await self.Client.Main(self.SavePath, self.Command)  # 发送命令到服务器

    async def UpApp(self):
        return self.main_box

    async def Start(self):
        """
        启动
        :return:
        """
        return self.main_box



class TCPAPP(toga.App):
    def startup(self):
        """Construct and show the Toga application.

        Usually, you would add your application to a main content box.
        We then create a main window (with a name matching the app), and
        show the main window.
        """
        main_box = toga.Box()
        App = RunApp(main_box)
        #main_box=App.Start()
        self.main_window = toga.MainWindow(title=self.formal_name)
        self.main_window.content = App.main_box  # 将main_box设置为主窗口内容
        self.main_window.show()


def main():
    return TCPAPP()
